# -*- coding:utf-8 -*-
# title           :
# description     :
# author          :Python超人/三月三提供
# date            :2024-05-01
# link            :https://gitcode.net/pythoncr/
# python_version  :3.9
# ==============================================================================
from 神笔码靓.神笔库.通用工具集 import *
from 神笔码靓.神笔库.元素库 import 元素
from 神笔码靓.神笔库.定义集 import *


def 滤镜警告(func):
    def wrapper(*args, **kwargs):
        滤镜名称 = func.__name__
        key = f"{滤镜名称}_已经警告"
        if not hasattr(滤镜工具集, key):
            消息 = f"注意：'{滤镜名称}'不支持无限高清"
            # print(f"\033[91m{消息}\033[0m")
            打印黄色消息(消息)
            setattr(滤镜工具集, key, True)

        return func(*args, **kwargs)

    return wrapper


class 滤镜工具集:
    """
    参考: https://www.51cto.com/article/652525.html
    <filter>标签用来定义滤镜，滤镜必须含有id属性来标识滤镜。图形元素通过filter=url(#id)来引用滤镜。
    feBlend:类似于CSS blend modes，描述了图像通过混合模式进行交互
    feComponentTransfer: 改变个人对RGBA通道的总称（如feFuncG）
    feComposite:一个原始的过滤器，定义像素图像交互方式
    feConvolveMatrix ：这个过滤器规定像素与他近邻将关闭交互（如：模糊、锐化）
    feDiffuseLighting ：定义了一个光源
    feDisplacementMap : 使用另一个输入值(in2)取代一个图像的像素值(in)
    feFlood : 完成过滤器的填充区域指定的颜色和alpha等级
    feGaussianBlur :输入的模糊值和标准值的偏差
    feImage ：使用其他的过滤器(像feBlend或feComposite)
    feMerge ： 允许异步过滤效果应用，而不是分层
    feMorphology : 削弱或扩张源图像
    feOffset ：用来创建阴影
    feSpecularLighting : 通过alpha创建凹凸贴图，又将其称之为”镜面”(Phong Reflection Model)
    feTile : 指图像如何重复填补空间
    feTurbulence : 允许创建纹理
    """

    def __init__(self, 高清图):
        from 神笔码靓.神笔库.神笔绘图板 import 神笔绘图板
        self.高清图: 神笔绘图板 = 高清图

    def _create_filter(self, id_=None):
        if id_ is None:
            id_ = 元素.生成元素id(t="f")
        filter_ = self.高清图.dwg.filter(id_=id_)
        return filter_

    def _set_filter(self, SVG元素, filter_name, get_filter_fun):
        if not hasattr(self, filter_name):
            dwg = self.高清图.dwg
            filter_ = get_filter_fun()
            # 将滤镜添加到SVG定义中
            dwg.defs.add(filter_)
            setattr(self, filter_name, filter_)
        else:
            filter_ = getattr(self, filter_name)

        SVG元素["filter"] = filter_.get_funciri()

    @滤镜警告
    def 形态(self, SVG元素, 形态操作='膨胀', 操作值='2'):
        # operator=erode 侵蚀/腐蚀, dilate 膨胀
        if 形态操作 == "膨胀":
            形态操作 = "dilate"
        else:
            形态操作 = "erode"

        filter_name = f"morphology_{形态操作}_{操作值}"

        def get_filter_fun():
            filter_ = self._create_filter()
            morphology = filter_.feMorphology(in_='SourceGraphic', operator=形态操作, radius=操作值, result=形态操作.upper())
            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 模糊(self, SVG元素, 模糊度=0):
        filter_name = f"blur_{模糊度}"

        def get_filter_fun():
            filter_ = self._create_filter()
            blur = filter_.feGaussianBlur(in_='SourceGraphic', stdDeviation=模糊度, result='blur')
            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 颜色滤镜(self, SVG元素, 颜色, x, y, 宽="100%", 高="100%", 透明度=0.5):
        filter_name = f"flood_{颜色}_{x}_{y}_{透明度}"

        def get_filter_fun():
            filter_ = self._create_filter()

            # 创建flood效果
            flood = filter_.feFlood(result="floodFill", x=x, y=y, width=宽, height=高, flood_color=颜色,
                                    flood_opacity=透明度)
            k2 = 1 - 透明度
            if k2 < 0:
                k2 = 0
            # blend = filter_.feBlend(in_='SourceGraphic', in2='floodFill', mode='multiply')
            composite = filter_.feComposite(in_="SourceGraphic", in2="floodFill",
                                            operator="arithmetic", k1="2", k2=k2, k3="0", k4="0")
            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 光照(self, SVG元素, 光源位置, 光源颜色=颜色.黄色):
        # specularExponent指的是镜面的光洁程度,镜面越光洁那么光线的漫反射就越小
        filter_name = f"_{光源位置}_{光源颜色}"

        def get_filter_fun():
            filter_ = self._create_filter()
            x, y, z = 光源位置
            diffuse_lighting = filter_.feDiffuseLighting(in_="SourceGraphic", result="light", lighting_color=光源颜色)
            point_light = diffuse_lighting.fePointLight(x=x, y=y, z=z)
            # diffuse_lighting.add(point_light)

            # Create feComposite element with specified parameters
            composite = filter_.feComposite(in_="SourceGraphic", in2="light", operator="arithmetic", k1="1", k2="0",
                                            k3="0", k4="0")
            # filter_.add(composite)

            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 扰动(self, SVG元素, 基频=0.05, 八度音阶数=2):
        """

        :param SVG元素:
        :param 基频: 滤镜基元的噪声函数的基频参数
        :param 八度音阶数: 一个八度音阶是由其频率和振幅定义的噪声函数。一个湍流是通过积累几个具有递增频率和递减振幅的八度音阶而构建的。八度音阶数量越高，噪声看起来就越自然。不过，更多的八度音阶也需要更多的计算，从而对性能产生负面影响。
        :return:
        """
        filter_name = f"_{基频}_{八度音阶数}"

        def get_filter_fun():
            filter_ = self._create_filter()
            # <feDropShadow dx="5" dy="5" stdDeviation="5" flood-color="#f00a" />
            turbulence = filter_.feTurbulence(type="turbulence", baseFrequency=基频, numOctaves=八度音阶数,
                                              result="turbulence")
            displacement_map = filter_.feDisplacementMap(in2="turbulence", in_='SourceGraphic', scale="50",
                                                         xChannelSelector="R", yChannelSelector="G")
            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 立体阴影(self, SVG元素, 偏移=(2, 2), 阴影模糊度=2, 灯光颜色=颜色.白色, 灯光位置=(-50, -100, 200)):
        """
        参考：https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Filter_effects
        :param SVG元素:
        :param 偏移:
        :param 阴影模糊度:
        :param 灯光颜色:
        :param 灯光位置:
        :return:
        """

        filter_name = f"3d_shadow_{偏移}_{阴影模糊度}_{灯光位置}"

        def get_filter_fun():
            filter_ = self._create_filter()
            # 创建模糊效果
            blur = filter_.feGaussianBlur(in_='SourceAlpha', stdDeviation=阴影模糊度, result='blur')

            # 设置偏移量
            dx, dy = 偏移
            offsetBlur = filter_.feOffset(in_='blur', dx=dx, dy=dy, result='offsetBlur')

            # 创建镜面反射效果
            # in_参数：指定输入源为之前创建的模糊效果
            # surfaceScale参数：指定表面的缩放比例
            # specularConstant参数：指定镜面高光的强度
            # specularExponent参数：指定镜面高光的指数
            # lighting_color参数：指定光照颜色
            # result参数：指定结果的名称为specOut

            # surfaceScale = "<数字>"
            # 当Ain = 1时表面的高度。
            # 如果未指定该属性，则效果相当于指定了值1。
            # 可动画：是。
            # specularConstant = "<数字>"
            # Phong光照模型中的ks。在SVG中，这可以是任何非负数。
            # 如果未指定该属性，则效果相当于指定了值1。
            # 可动画：是。
            # specularExponent = "<数字>"
            # 镜面反射项的指数，越大越“闪亮”。范围从1.0到128.0。
            # 如果未指定该属性，则效果相当于指定了值1。
            # 可动画：是。
            # kernelUnitLength = "<数字-可选-数字>"
            # 第一个数字是值。第二个数字是值。如果未指定值，它默认为与相同的值。指示当前滤镜单位（即由‘primitiveUnits’属性值确定的单位）下表面法线计算公式中dx和dy的预期距离。通过为‘kernelUnitLength’指定值，核变得在一个可缩放的、抽象坐标系统中定义。如果未指定‘kernelUnitLength’，那么dx和dy值应该代表相对于给定(x,y)位置的非常小的增量，在某些情况下可能被实现为中间图像离屏位图中的一个像素，这是一个基于像素的坐标系统，因此可能不可缩放。为了在不同显示媒体和用户代理之间保持一定程度的一致性，至少需要为‘filterRes’和‘kernelUnitLength’中的一个提供值。有关中间图像的讨论在引言中以及‘filterRes’属性的描述中。
            # 负值或零值是一个错误（见错误处理）。
            # 可动画：是。
            specular_lighting = filter_.feSpecularLighting(in_='blur', surfaceScale="5", specularConstant=".75",
                                                           specularExponent="20", lighting_color=灯光颜色,
                                                           result="specOut")

            # x = "<数字>"
            # 光源在坐标系统中的X位置，该坐标系统由“filter”元素上的‘primitiveUnits’属性建立。
            # 如果未指定该属性，则效果相当于指定了值0。
            # y = "<数字>"
            # 光源在坐标系统中的Y位置，该坐标系统由“filter”元素上的‘primitiveUnits’属性建立。
            # 如果未指定该属性，则效果相当于指定了值0。
            # z = "<数字>"
            # 光源在坐标系统中的Z位置，该坐标系统由“filter”元素上的‘primitiveUnits’属性建立，假设在初始坐标系统中，正Z轴朝向观看内容的人，并假设Z轴上的一个单位等于X和Y轴上的一个单位。
            # 如果未指定该属性，则效果相当于指定了值0。
            light_x, light_y, light_z = 灯光位置  # x="-50", y="-100", z="200"(-50, -100, 200)
            fePointLight = specular_lighting.fePointLight(x=light_x, y=light_y, z=light_z)
            # specular_lighting.add()
            # filter.add(specular_lighting)

            # 合并镜面反射效果
            feComposite1 = filter_.feComposite(in_='specOut', in2='SourceAlpha', operator='in', result='specOut')
            # filter.add(feComposite1)
            feComposite2 = filter_.feComposite(in_='SourceGraphic', in2='specOut', operator='arithmetic',
                                               k1="0", k2="1", k3="1", k4="0", result='litPaint')
            # filter.add(feComposite2              )

            # 合并模糊效果和镜面反射效果
            filter_.feMerge(layernames=["offsetBlur", "litPaint"])

            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 阴影(self, SVG元素, 偏移=(2, 2), 模糊度=2):
        filter_name = f"shadow_{偏移}_{模糊度}"

        def get_filter_fun():
            filter_ = self._create_filter()
            blur = filter_.feGaussianBlur(in_='SourceAlpha', stdDeviation=模糊度, result='blur')
            dx, dy = 偏移
            # feOffset滤镜使用 in 拿到了上一步的结果 result = ‘blur’，然后做了一个简单的位移。
            offsetBlur = filter_.feOffset(in_='blur', dx=dx, dy=dy, result='offsetBlur')
            # "offsetBlur" 表示了上述两个滤镜的最终输出结果 offsetBlur，也就是阴影的部分
            # “SourceGraphic” 关键词表示图形元素自身将作为 原语的原始输入
            filter_.feMerge(layernames=["offsetBlur", "SourceGraphic"])
            return filter_

        self._set_filter(SVG元素, filter_name, get_filter_fun)

    @滤镜警告
    def 反色(self, SVG元素):
        filter_name = f"invert-colors"

        def get_filter_fun():
            invert_filter = self._create_filter()
            color_matrix = invert_filter.feColorMatrix(type='matrix',
                                                       values='-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0')
            # 将feColorMatrix元素添加到滤镜中
            invert_filter.add(color_matrix)
            return invert_filter

        self._set_filter(SVG元素, filter_name, get_filter_fun)
